package com.longma.server.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.longma.server.dao.SysUserDAO;
import com.longma.server.entity.SysUser;
import com.longma.server.pojo.vo.AlarmCountVO;
import com.longma.server.security.JwtTokenUtil;
import com.longma.server.service.IAlarmRecordService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author： zy
 * @Date：2021/5/21
 * @Description：webSocket服务端
 */
@Component
@ServerEndpoint("/api/websocket/{token}")
@Slf4j
@Data
public class WebSocketServer implements ApplicationContextAware {
    /**
     *  用于存所有的连接服务的客户端，这个对象存储是安全的
     *  key格式 token
     */
    private static ConcurrentHashMap<String,WebSocketServer> webSocketServerMap = new ConcurrentHashMap<>();

    /**
     * 标识当前连接客户端的用户  由于相同用户不同时间登录生成的token不同，所以可以实现统一用户异地登录
     */
    private String token;

    /**
     * token对应的用户loginName
     */
    private String loginName;

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 上下文
     */
    private static ApplicationContext context;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token")String token) throws IOException {
        // 校验token合法性
        try {
            this.loginName = getLoginName(token);
        }catch(Exception e){
            log.error("websocket onOpen error "+e.getMessage(),e);
        }
        if(this.loginName == null){
            session.close();
            return;
        }

        log.info("webSocket login user: " + this.loginName);
        this.session = session;
        this.token = token;
        webSocketServerMap.put(token,this);
        log.info("有新窗口开始监听:"+token+"," + "当前连接人数为：={}", webSocketServerMap.size());

        // 因为无法注入进来 所以这么做
        IAlarmRecordService alarmRecordService = context.getBean(IAlarmRecordService.class);
        // 获取该用户的告警计数信息
        AlarmCountVO alarmCountVO =  alarmRecordService.getAlarmCount(loginName);
        // 开始发送数据
        this.sendMessage(JSON.toJSONString(alarmCountVO));
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketServerMap.remove(this.token);
        log.info(this.token + "连接关闭！当前在线人数为" + webSocketServerMap.size());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session
     **/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口"+token+"的信息:"+message);
    }

    /**
     * @param session
     * @param e
     */
    @OnError
    public void onError(Session session, Throwable e) {
        log.error("发生错误 " + e.getMessage(),e);
    }

    /**
     * 获取所有在线的客户端
     */
    public static Map<String,WebSocketServer> getWebSocketServerMap(){
        return webSocketServerMap;
    }

    /**
     * 群发
     * @param message
     */
    public void groupSending(String message){
        for (String token : webSocketServerMap.keySet()){
            try {
                webSocketServerMap.get(token).session.getBasicRemote().sendText(message);
            }catch (Exception e){
                log.error("groupSending " + e.getMessage(),e);
            }
        }
    }

    /**
     * 指定发送
     * @param token
     * @param message
     */
    public void appointSending(String token,String message){
        try {
            webSocketServerMap.get(token).session.getBasicRemote().sendText(message);
        }catch (Exception e){
            log.error("appointSending " + e.getMessage(),e);
        }
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message){
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("sendMessage " + e.getMessage(),e);
        }
    }

    /**
     * 校验token，并获取loginName
     * token无效返回null
     */
    private String getLoginName(String token){
        // 校验token
        if(StrUtil.isBlank(token)){
            return null;
        }
        // 获取用户名
        String loginName = JwtTokenUtil.getUsernameFromToken(token);

        //参数校验
        if (StrUtil.isBlank(loginName)){
            return null;
        }

        // 因为无法注入进来 所以这么做
        SysUserDAO sysUserDAO = context.getBean(SysUserDAO.class);
        SysUser loginUser = sysUserDAO.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getLoginName,loginName));
        if (loginUser == null) {
            return null;
        }

        //验证token有效性 用户名是否正确、token是否过期
        if( JwtTokenUtil.validateToken(token, loginName)){
            return loginName;
        }
        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)  {
        context = applicationContext;
    }
}
